/**
 *  std::lock_guard: 这是一个轻量级的RAII包装器，它在构造时自动锁定互斥量，在析构时自动解锁互斥量。
 *  这样可以确保即使有异常抛出，互斥量也总是会被解锁。std::lock_guard不支持手动解锁或重新锁定
 *  std::mutex::lock()和std::mutex::unlock(): 这是最基本的锁定和解锁互斥量的方式。你需要手动调用lock()和unlock()。
 *  这种方式的问题是，如果在lock()和unlock()之间的代码抛出了异常，那么unlock()可能永远不会被调用，从而导致死锁。
 *  std::unique_lock: 这是一个更灵活的RAII包装器，它也在构造时自动锁定互斥量，在析构时自动解锁互斥量。
 *  但与std::lock_guard不同，std::unique_lock支持手动解锁和重新锁定，
 *  还支持延迟锁定（即在构造时不锁定互斥量，稍后再锁定）。std::unique_lock还可以与std::condition_variable一起使用。
 *
 *
 */
#include <iostream>
#include <thread>
#include <mutex>
#include <functional>

std::mutex mtx; // 全局互斥量
#define BLOCK print_block

void print_block(int n, char c)
{
    mtx.lock();
    for (int i = 0; i < n; ++i)
    {
        std::cout << c;
    }
    std::cout << '\n';
    mtx.unlock();
}

void print_block2(int n, char c)
{
    std::lock_guard<std::mutex> lock(mtx);
    for (int i = 0; i < n; ++i)
    {
        std::cout << c;
    }
    std::cout << '\n';
}

void print_block3(int n, char c)
{
    for (int i = 0; i < n; ++i)
    {
        std::cout << c;
    }
    std::cout << '\n';
}

void print_numbers(int a, int b, int c)
{
    std::cout << a << ", " << b << ", " << c << std::endl;
}

int main()
{
    // 使用std::bind绑定print_numbers的第一个和第三个参数
    auto fn = std::bind(print_numbers, 1, std::placeholders::_1, 3);

    // 调用fn，只需要提供一个参数，因为其他两个参数已经被绑定
    fn(2); // 输出：1, 2, 3
           // 使用std::bind绑定print_numbers的所有参数
    auto fn2 = std::bind(print_numbers, 3, 4, 5);

    // 调用fn，只需要提供一个参数，因为其他两个参数已经被绑定
    fn2(); // 输出：3, 4, 5
    std::thread th1(BLOCK, 50, '*');
    std::thread th2(BLOCK, 50, '$');
    std::thread th3(BLOCK, 50, '+');
    std::thread th4(BLOCK, 50, '-');
    // std::thread th3(std::bind(print_block, 50, '+'));
    // std::thread th4(std::bind(print_block, 50, '-'));
    // 输出的顺序不一定是 *¥+-

    th1.join();
    th2.join();
    th3.join();
    th4.join();
    std::cout << "hello world" << std::endl;
    return 0;
}